﻿using System;
using System.Data;
using System.Security.Cryptography;
using System.IO;
using System.Text;

namespace Cryptography
{
    public class CryptographyManager
    {
        private readonly ICryptoTransform _encryptorTransform;
        private readonly ICryptoTransform _decryptorTransform;
        private readonly UTF8Encoding _utfEncoder;

        public CryptographyManager()
        {
            _utfEncoder = new UTF8Encoding();
        }

        public CryptographyManager(byte[] key, byte[] vector)
            : this()
        {
            var rm = new RijndaelManaged();

            _encryptorTransform = rm.CreateEncryptor(key, vector);
            _decryptorTransform = rm.CreateDecryptor(key, vector);
        }

        public CryptographyManager(string key, string vector)
            : this()
        {
            var bKey = StringToByteArray(key);
            var bVector = StringToByteArray(vector);

            var rm = new RijndaelManaged();
            _encryptorTransform = rm.CreateEncryptor(bKey, bVector);
            _decryptorTransform = rm.CreateDecryptor(bKey, bVector);
        }

        public string Decrypt(string encryptedString)
        {
            return DecodeFrom64(DecryptByteArray(StringToByteArray(encryptedString)));
        }

        public string Encrypt(string stringToEncrypt)
        {
            return EncodeTo64(ByteArrayToString(EncryptToByteArray(stringToEncrypt)));
        }

        public string GetHash(string textValue)
        {
            var strBytes = Encoding.UTF8.GetBytes(textValue);
            var sha = new SHA512Managed();
            var hashBytes = sha.ComputeHash(strBytes);

            return Convert.ToBase64String(hashBytes);
        }

        public bool ValidateHash(string textValueHash, string textValue)
        {
            var strPswd = GetHash(textValue);
            return string.Compare(strPswd, textValueHash) == 0;
        }

        #region Helpers
        private byte[] StringToByteArray(string str)
        {
            return _utfEncoder.GetBytes(str);
        }

        private string ByteArrayToString(byte[] byteArray)
        {
            return _utfEncoder.GetString(byteArray);
        }

        private byte[] EncryptToByteArray(string textValue)
        {
            var bytes = StringToByteArray(textValue);
            var memoryStream = new MemoryStream();

            #region Write the decrypted value to the encryption stream
            var cs = new CryptoStream(memoryStream, _encryptorTransform, CryptoStreamMode.Write);
            cs.Write(bytes, 0, bytes.Length);
            cs.FlushFinalBlock();
            #endregion

            #region Read encrypted value back out of the stream
            memoryStream.Position = 0;
            var encrypted = new byte[memoryStream.Length];
            memoryStream.Read(encrypted, 0, encrypted.Length);
            #endregion

            cs.Close();
            memoryStream.Close();

            return encrypted;
        }

        private string DecryptByteArray(byte[] encryptedValue)
        {
            #region Write the encrypted value to the decryption stream
            var encryptedStream = new MemoryStream();
            var decryptStream = new CryptoStream(encryptedStream, _decryptorTransform, CryptoStreamMode.Write);
            decryptStream.Write(encryptedValue, 0, encryptedValue.Length);
            decryptStream.FlushFinalBlock();
            #endregion

            #region Read the decrypted value from the stream.
            encryptedStream.Position = 0;
            var decryptedBytes = new Byte[encryptedStream.Length];
            encryptedStream.Read(decryptedBytes, 0, decryptedBytes.Length);
            encryptedStream.Close();
            #endregion

            return ByteArrayToString(decryptedBytes);
        }

        private string EncodeTo64(string toEncode)
        {
            byte[] toEncodeAsBytes
                  = _utfEncoder.GetBytes(toEncode);
            string returnValue
                  = Convert.ToBase64String(toEncodeAsBytes);
            return returnValue;
        }

        private string DecodeFrom64(string encodedData)
        {
            byte[] encodedDataAsBytes
                = Convert.FromBase64String(encodedData);
            string returnValue =
               _utfEncoder.GetString(encodedDataAsBytes);
            return returnValue;
        }
        #endregion
    }

}
